package com.gitee.wsl.common.chart.render.foreground

import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import com.gitee.wsl.common.chart.ChartEnum
import com.gitee.wsl.common.chart.model.PlotDot
import com.gitee.wsl.common.draw.area.Viewport
import com.gitee.wsl.common.chart.render.foreground.border.BorderDrawableReader
import com.gitee.wsl.common.chart.render.shape.plot.renderDot
import com.gitee.wsl.common.chart.sample.XChart
import com.gitee.wsl.common.ui.draw.type.DotStyle
import com.gitee.wsl.common.ui.draw.type.HorizontalAlign
import com.gitee.wsl.common.ui.draw.type.VerticalAlign
import com.gitee.wsl.android.ui.ext.type.textHeight
import com.gitee.wsl.android.ui.ext.type.textWidth
import com.gitee.wsl.mathematics.geometry.shape.Rectangle

abstract class AbstractChartLegend<T>(val mXChart: XChart): Rectangle() {
    //数据集的说明描述与图这间的空白间距
    var mMargin = 10f

    //数据集的说明描述画笔
    val mKeyPaint: Paint by lazy {
        Paint().apply {
            color = Color.BLACK
            isAntiAlias = true
            textSize = 15f
        }
    }

    //是否显示图例
    var isVisible = false

    //图例起始偏移多少距离
    var mOffsetX = 0.0f
    var mOffsetY = 0.0f

    //行间距
    var mRowSpan = 10.0f
    var mColSpan = 10.0f

    //图例方向
    /*
		private ChartEnum.LegendType mLegendType = ChartEnum.LegendType.COLUMN;
		private ChartEnum.HorizontalAlign mHorizontalAlign = ChartEnum.HorizontalAlign.RIGHT;
		private ChartEnum.VerticalAlign mVerticalAlign = ChartEnum.VerticalAlign.MIDDLE;
		 */
    var mLegendType = ChartEnum.LegendType.ROW
    var mHorizontalAlign = HorizontalAlign.LEFT
    var mVerticalAlign = VerticalAlign.TOP

    //box
    val borderReader : BorderDrawableReader by lazy{
        BorderDrawableReader(this)
    }

    var mShowBox = true
        set(value) {
            field=value
            if(value) {
                mShowBoxBorder = true
                mShowBackground = true
            }
        }

    var mShowBoxBorder by borderReader::isShowBorder
    var mShowBackground by borderReader::isShowBackground

    //private static final String TAG = "PlotLegendRender";
    private var mPlotViewport: Viewport? = null

    private var mKeyLabelX = 0.0f
    private var mKeyLabelY = 0.0f

    /////////////////////////////////////
    var mLstDotStyle = mutableListOf<PlotDot>()
    var mLstKey  = mutableListOf<String>()
    var mLstColor = mutableListOf<Int>()

    private var mRectWidth = 0.0f
    private var mRectHeight = 0.0f

    var mMapID = LinkedHashMap<Int, Int>()

    private val mIsLnChart = false
    private val mPaintLine: Paint? = null

    enum class EnumChartType {
        AXIS, CIR, LN, RD
    }

    var mType = EnumChartType.AXIS


    fun draw(canvas: Canvas, dataSet: List<T>):Boolean{
        if (!isVisible) return false
        refreshLst()
        convertArrayKey(dataSet)
        render(canvas)
        return true
   }

    abstract fun convertArrayKey( dataSet:List<T>)

    private  fun initEnv() {
        mKeyLabelY = 0.0f
        mKeyLabelX = mKeyLabelY
        mRectHeight = 0.0f
        mRectWidth = mRectHeight
    }

     open fun render(canvas: Canvas) {
        if (null == mPlotViewport) mPlotViewport = mXChart.plotViewport
        calcContentRect()
        getStartXY()
        drawLegend(canvas)
    }

    private  fun getRectWidth(): Float {
        var rectWidth = 0.0f
        val textHeight: Float = getLabelTextHeight()
        rectWidth = if (mIsLnChart) {
            2 * textHeight
        } else {
            textHeight / 2 + textHeight
        }
        return rectWidth
    }

    private  fun calcContentRect() {
        val countDots =  mLstDotStyle.size 
        val countText =  mLstKey.size 
        if (0 == countText && 0 == countDots) return
        //int count = (countText > countDots)?countText:countDots;
        var text = ""
        val textHeight: Float = getLabelTextHeight()
        var row = 1
        mMapID.clear()
        val areaWidth = mPlotViewport!!.width - 2 * mMargin
        val rectWidth = getRectWidth()
        var rowWidth = 0.0f
        var maxHeight = textHeight
        var maxWidth = 0.0f
        for (i in 0 until countText) {
            if (countDots > i) {
                val plot = mLstDotStyle[i]
                if (mIsLnChart) {
                    rowWidth += rectWidth + mColSpan
                } else {
                    if (plot.dotStyle != DotStyle.HIDE) rowWidth += rectWidth + mColSpan
                }
            }
            text = mLstKey[i]
            val labelWidth: Float = getLabelTextWidth(text)
            rowWidth += labelWidth
            when (mLegendType) {
                ChartEnum.LegendType.ROW -> if (rowWidth.compareTo(areaWidth) == 1
                ) {    //换行
                    rowWidth = rectWidth + mColSpan + labelWidth
                    maxHeight += textHeight + mRowSpan
                    row++
                } else {
                    rowWidth += mColSpan
                    if (rowWidth.compareTo(maxWidth) == 1) maxWidth = rowWidth
                }

                ChartEnum.LegendType.COLUMN -> {
                    if (rowWidth.compareTo(maxWidth) == 1) maxWidth = rowWidth
                    maxHeight += textHeight + mRowSpan
                    rowWidth = 0.0f
                    row++
                }
                else -> {}
            }
            mMapID[i] = row
        }
        mRectWidth = maxWidth + 2 * mMargin
        mRectHeight = maxHeight + 2 * mMargin
        if (ChartEnum.LegendType.COLUMN == mLegendType) mRectHeight -= 2 * mRowSpan
    }


    private  fun getStartXY() {
        var mBoxLineSize = BOX_LINE_SIZE.toFloat()
        if (!mShowBox) mBoxLineSize = 0.0f
        when (mHorizontalAlign) {
            HorizontalAlign.LEFT -> {
                mKeyLabelX = if (EnumChartType.CIR == mType) {
                    mXChart.left + mOffsetX
                } else {
                    mPlotViewport!!.left + mOffsetX
                }
                mKeyLabelX += mBoxLineSize
            }

            HorizontalAlign.CENTER -> mKeyLabelX =
                mXChart.left + (mXChart.width - mRectWidth) / 2 + mOffsetX

            HorizontalAlign.RIGHT -> {
                mKeyLabelX = if (EnumChartType.CIR == mType) {
                    mXChart.right - mOffsetX - mRectWidth
                } else {
                    mPlotViewport!!.right - mOffsetX - mRectWidth
                }
                mKeyLabelX -= mBoxLineSize
            }

            else -> {}
        }
        when (mVerticalAlign) {
            VerticalAlign.TOP -> if (ChartEnum.LegendType.COLUMN == mLegendType) {
                mKeyLabelY = mPlotViewport!!.top + mOffsetY
                mKeyLabelY += mBoxLineSize
            } else {
                mKeyLabelY = mPlotViewport!!.top - mRectHeight - mOffsetY
                mKeyLabelY -= mBoxLineSize
            }

            VerticalAlign.MIDDLE -> mKeyLabelY =
                mPlotViewport!!.top + (mPlotViewport!!.height - mRectHeight) / 2

            VerticalAlign.BOTTOM -> if (ChartEnum.LegendType.COLUMN == mLegendType) {
                mKeyLabelY = mXChart.bottom + mOffsetY
                mKeyLabelY += mXChart.borderWidth.toFloat()
                mKeyLabelY += mBoxLineSize
            } else {
                mKeyLabelY = mXChart.bottom - mRectHeight - mOffsetY
                mKeyLabelY -= mXChart.borderWidth.toFloat()
                mKeyLabelY -= mBoxLineSize
            }

            else -> {}
        }
    }


    private  fun drawLegend(canvas: Canvas) {
        val countDots =  mLstDotStyle.size 
        val countText =  mLstKey.size
        if (0 == countText && 0 == countDots) return
        val countColor =  mLstColor.size
        val currDotsX = mKeyLabelX + mMargin
        var currRowX = currDotsX
        var currRowY = mKeyLabelY + mMargin
        val textHeight: Float = getLabelTextHeight()
        val rectWidth = getRectWidth() //2 * textHeight;
        var currRowID = 0
        val iter: Iterator<*> = mMapID.entries.iterator()

        //背景
        drawBox(canvas)

        //图例
        while (iter.hasNext()) {
            val (key, value) = iter.next() as Map.Entry<*, *>
            val id = key as Int
            val row = value as Int //行号
            if (row > currRowID) //换行
            {
                if (0 < currRowID) currRowY += textHeight + mRowSpan
                currRowX = mKeyLabelX + mMargin
                currRowID = row
            }

            //颜色
            if (countColor > id) {
                mKeyPaint.color = mLstColor[id]
                if (mIsLnChart) mPaintLine!!.color = mLstColor[id]
            } else {
                mKeyPaint.color = Color.BLACK
                if (mIsLnChart) mPaintLine!!.color = Color.BLACK
            }
            if (countDots > id) {
                val plot = mLstDotStyle[id]
                if (mIsLnChart) //line
                {
                    canvas.drawLine(
                        currRowX, currRowY + textHeight / 2,
                        currRowX + rectWidth, currRowY + textHeight / 2,
                        mPaintLine!!
                    )
                    canvas.renderDot(
                         plot,
                        currRowX + rectWidth / 2,
                        currRowY + textHeight / 2,
                        mKeyPaint
                    )
                    currRowX += rectWidth + mColSpan
                } else {
                    if (plot.dotStyle != DotStyle.HIDE) {
                        canvas.renderDot(
                             plot,
                            currRowX + rectWidth / 2,
                            currRowY + textHeight / 2,
                            mKeyPaint
                        )
                        currRowX += rectWidth + mColSpan
                    }
                }
            }
            val label = mLstKey[id]
            if ("" !== label) canvas.drawText(
                label,
                currRowX,
                currRowY + textHeight,
                mKeyPaint
            )
            currRowX += this.getLabelTextWidth(label)
            currRowX += mColSpan
        }
        mMapID.clear()
        clearLst()
    }

     protected fun getLabelTextHeight(): Float{
         return mKeyPaint.textHeight
     }


    protected fun getLabelTextWidth(label: String): Float{
        return mKeyPaint.textWidth(label)
    }

    private  fun clearLst() {
            mLstDotStyle.clear()
            mLstKey.clear()
            mLstColor.clear()
    }


    private  fun drawBox(canvas: Canvas) {
        if (!mShowBox) return
        val rect = RectF()
        rect.left = mKeyLabelX
        rect.right = mKeyLabelX + mRectWidth
        rect.top = mKeyLabelY
        rect.bottom = mKeyLabelY + mRectHeight
        borderReader.renderRect(canvas, rect, mShowBoxBorder, mShowBackground)
    }

    private  fun refreshLst() {
        initEnv()
        mLstKey.clear()
        mLstDotStyle.clear()
        mLstColor.clear()
    }


    ////////////////////////////////


    protected fun isDrawKey(key: String): Boolean {
        return !("" === key || key.isEmpty())
    }


    companion object{
        //此处的5.f相当于this.getBox().getLinePaint()的高度
        private val BOX_LINE_SIZE = 5
    }

}